/* NOTES:
 * - build tree from XML file
 * - pass object subtrees to plugin-provided objdata loaders
 * - set objdata to object container obj_set_objdata
 */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <xmltree.h>
#include "scene.h"

#define SCENE_IMPL_SRC
#include "scene_impl.h"

static int load_materials(struct scene *s, struct xml_node *tree);
static int load_mat(struct material *mat, struct xml_node *mnode);
static int load_objects(struct scene *s, struct xml_node *tree);
static int load_lights(struct scene *s, struct xml_node *tree);

int scn_read(struct scene *s, const char *fname)
{
	FILE *fp;
	int res;

	if(!(fp = fopen(fname, "rb"))) {
		fprintf(stderr, "failed to read scene: %s: %s\n", fname, strerror(errno));
		return -1;
	}
	res = scn_read_file(s, fp);
	fclose(fp);
	return res;
}

int scn_read_file(struct scene *s, FILE *fp)
{
	struct xml_node *tree;
	struct xml_attr *attr;

	if(!(tree = xml_read_tree_file(fp))) {
		return -1;
	}
	if(strcmp(tree->name, "scene") != 0) {
		xml_free_tree(tree);
		fprintf(stderr, "illegal scene file\n");
		return -1;
	}

	if((attr = xml_get_attr(tree, "name"))) {
		scn_set_name(s, attr->str);
	} else {
		scn_set_name(s, "scene");
	}

	load_materials(s, tree);
	
	if(load_objects(s, tree) == -1) {
		xml_free_tree(tree);
		return -1;
	}

	if(load_lights(s, tree) == -1) {
		xml_free_tree(tree);
		return -1;
	}

	xml_free_tree(tree);
	return 0;
}

static int load_materials(struct scene *s, struct xml_node *tree)
{
	struct xml_node *node;

	node = tree->chld;
	while(node) {
		if(strcmp(node->name, "material") == 0) {
			struct material *mat;

			if(!(mat = mat_create())) {
				return -1;
			}
			load_mat(mat, node);
			scn_add_material(s, mat, mat_get_name(mat));
		}
		node = node->next;
	}
	return 0;
}

static int load_mat(struct material *mat, struct xml_node *mnode)
{
	struct xml_attr *attr;
	struct xml_node *iter;

	if((attr = xml_get_attr(mnode, "name"))) {
		mat_set_name(mat, attr->str);
	}

	if((attr = xml_get_attr(mnode, "shader"))) {
		mat_set_shader(mat, attr->str);
	}

	iter = mnode->chld;
	while(iter) {
		struct mattr *mattr;
		struct xml_node *node = iter;
		iter = iter->next;

		if(strcmp(node->name, "mattr") != 0) {
			fprintf(stderr, "warning: ignoring non-mattr element inside material: %s\n", node->name);
			continue;
		}

		if(!(mattr = malloc(sizeof *mattr))) {
			perror("malloc failed");
			continue;
		}
		memset(mattr, 0, sizeof *mattr);

		if((attr = xml_get_attr(node, "name"))) {
			mattr_set_name(mattr, attr->str);
		} else {
			mattr_set_name(mattr, "unnamed");
		}

		if((attr = xml_get_attr(node, "value"))) {
			if(attr->type == ATYPE_VEC) {
				mattr->val = v4_cons(attr->vval[0], attr->vval[1], attr->vval[2], 1.0);
			} else {
				mattr->val.x = mattr->val.y = mattr->val.z = attr->fval;
				mattr->val.w = 1.0;
			}
		}

		if((attr = xml_get_attr(node, "tex"))) {
			mattr_set_texture(mattr, attr->str);
		}
		
		if((attr = xml_get_attr(node, "texmat"))) {
			/* TODO */
		}

		mat_add_attr(mat, mattr);
	}

	return 0;
}

/* internal implementation function */
void _obj_impl_set_scene(struct object *obj, struct scene *scn);

static int load_objects(struct scene *s, struct xml_node *tree)
{
	struct xml_node *node;

	node = tree->chld;
	while(node) {
		if(strcmp(node->name, "object") == 0) {
			struct object *obj;

			if(!(obj = obj_create())) {
				return -1;
			}
			_obj_impl_set_scene(obj, s);	/* object loading need access to material lists */
			if(obj_from_xml(obj, node) == -1) {
				obj_free(obj);
				return -1;
			}
			scn_add_object(s, obj);
		}
		node = node->next;
	}
	return 0;
}

static int load_lights(struct scene *s, struct xml_node *tree)
{
	struct xml_node *node;

	node = tree->chld;
	while(node) {
		if(strcmp(node->name, "light") == 0) {
			struct light *lt;

			if(!(lt = lt_create())) {
				return -1;
			}
			if(lt_from_xml(lt, node) == -1) {
				lt_free(lt);
				return -1;
			}
			scn_add_light(s, lt);
		}
		node = node->next;
	}
	return 0;
}

int scn_write(struct scene *s, const char *fname)
{
	FILE *fp;
	int res;

	if(!(fp = fopen(fname, "wb"))) {
		fprintf(stderr, "failed to write scene: %s: %s\n", fname, strerror(errno));
		return -1;
	}
	res = scn_write_file(s, fp);
	fclose(fp);
	return res;
}

int scn_write_file(struct scene *s, FILE *fp)
{
	return -1; /* TODO */
}

